#include "fyp/shadows/DVSM.h"

//------------------------------------------------------------------------------
// additional header includes
#include "fyp/ObjectManager.h"
#include "fyp/ResourcePool.h"

#include "fyp/render/Renderer.h"

using namespace ming::fyp::render;

//------------------------------------------------------------------------------
namespace ming
{
namespace fyp
{
namespace shadows
{
	//--------------------------------------------------------------------------
	TDVSM::TDVSM()
	{
		mPendingShadowMapSize = 512;
	}
	
	//--------------------------------------------------------------------------
	TDVSM::~TDVSM()
	{
	}

	//--------------------------------------------------------------------------
	void TDVSM::Init(TResourcePool *resPool, TGuiManager *guiMgr)
	{
		resPool->Add<IShader>("DVSM.Apply", make_tuple( STR("../media/shader/DVSM.Apply.fx"), STR("Apply") ) );
		resPool->Add<IShader>("DVSM.Generate", make_tuple( STR("../media/shader/DVSM.Generate.fx"), STR("Generate") ) );
		resPool->Add<IShader>("DVSM.Blur", make_tuple( STR("../media/shader/DVSM.Blur.fx"), STR("Blur") ) );
	}

	//--------------------------------------------------------------------------
	void TDVSM::RenderScene(
		render::IRenderer *renderer,
		TResourcePool *resPool,
		TObjectManager *objMgr)
	{

		if(mPendingShadowMapSize > 0)
		{
			resPool->Remove<IRenderedTexture>("DVSM.SMap");
			resPool->Remove<IRenderedTexture>("DVSM.Temp");

			resPool->Add<IRenderedTexture>(
				"DVSM.SMap", 
				make_tuple( (size_t)IRenderedTexture::FLOAT16_ABGR,
							(size_t)mPendingShadowMapSize,
							(size_t)mPendingShadowMapSize,
							true) );

			resPool->Add<IRenderedTexture>(
				"DVSM.Temp", 
				make_tuple( (size_t)IRenderedTexture::FLOAT16_ABGR,
							(size_t)1024,
							(size_t)1024,
							true) );

			resPool->Get<IShader>("DVSM.Blur")->BindParam(
				"INV_INPUT_SIZE",
				&TVec2(1.0f / (float)1024, 0.5f / (float)1024 ),
				sizeof( TVec2) );

			resPool->Get<IRenderedTexture>("DVSM.SMap")->SetFilterMode(
				ITexture::POINT, ITexture::POINT, ITexture::POINT);

			resPool->Get<IRenderedTexture>("DVSM.SMap")->SetAddressMode(
				ITexture::CLAMP, ITexture::CLAMP, ITexture::CLAMP);

			resPool->Get<IRenderedTexture>("DVSM.Temp")->SetFilterMode(
				ITexture::POINT, ITexture::POINT, ITexture::POINT);

			resPool->Get<IRenderedTexture>("DVSM.Temp")->SetAddressMode(
				ITexture::CLAMP, ITexture::CLAMP, ITexture::CLAMP);

			mPendingShadowMapSize = -1;
		}

		GenerateShadowMap(renderer, resPool, objMgr);

		RenderSceneWithShadowMap(renderer, resPool, objMgr);
	}
	
	//--------------------------------------------------------------------------
	void TDVSM::Final(TResourcePool *resPool)
	{
		resPool->Remove<IShader>("DVSM.Apply");
		resPool->Remove<IShader>("DVSM.Generate");
		resPool->Remove<IShader>("DVSM.Blur");

		resPool->Remove<IRenderedTexture>("DVSM.SMap");
		resPool->Remove<IRenderedTexture>("DVSM.Temp");

	}

	//--------------------------------------------------------------------------
	void TDVSM::ChangeShadowMapSize(size_t sz)
	{
		mPendingShadowMapSize = (int)sz;
	}

	//--------------------------------------------------------------------------
	const char* TDVSM::GetDescription()
	{
		return "Deferred Variance Shadow Maps";
	}

	//--------------------------------------------------------------------------
	void TDVSM::GenerateShadowMap(
		render::IRenderer *renderer,
		TResourcePool *resPool,
		TObjectManager *objMgr)
	{
		IShader *shader = resPool->Get<IShader>("DVSM.Generate");

		renderer->BindRenderTarget(resPool->Get<IRenderedTexture>("DVSM.SMap"), NULL, NULL, NULL, true);
		renderer->ClearColor( TVec4(1.0f, 1.0f, 1.0f, 1.0f) );
		renderer->ClearDepth(1.0f);

		objMgr->RenderCasterObject(renderer, shader);
		objMgr->RenderReceiverObject(renderer, shader);
	}

	//--------------------------------------------------------------------------
	void TDVSM::RenderSceneWithShadowMap(
		render::IRenderer *renderer,
		TResourcePool *resPool,
		TObjectManager *objMgr)
	{
		IShader *shader = resPool->Get<IShader>("DVSM.Apply");
		
		renderer->BindRenderTarget(resPool->Get<IRenderedTexture>("DVSM.Temp"), NULL, NULL, NULL, true);
		renderer->ClearColor( TVec4(1.0f, 1.0f, 1.0f, 1.0f) );
		renderer->ClearDepth(1.0f);

		renderer->BindTexture(3, resPool->Get<IRenderedTexture>("DVSM.SMap") );

		objMgr->RenderCasterObject(renderer, shader);
		objMgr->RenderReceiverObject(renderer, shader);
		
		renderer->BindTexture(3, NULL);

		// blur the shadow map
		renderer->BindTexture(0, resPool->Get<IRenderedTexture>("DVSM.Temp") );
		renderer->RestoreDefaultRenderTarget();
		
		renderer->RenderScreenQuad( resPool->Get<IShader>("DVSM.Blur") );
	}
	
	//--------------------------------------------------------------------------
}
}
}
